﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace LprMiddleWare.HangWang
{
    public delegate void ImageReceivedHandler(object sender, NewImageEventArgs args);

    public delegate void DeviceStatusChangedHandler(object sender, DeviceStatusEventArgs args);

    public delegate void ErrorOccurredHandler(object sender, ErrorEventArgs args);


    public class HanWangCapturer : IDisposable
    {
        private bool _initialized;
        private HWRECVCALLBACK _callBack;
        public string DeviceId { get; set; }

        public event ImageReceivedHandler NewImageReceived;
        public event DeviceStatusChangedHandler DeviceStatusChanged;
        public event ErrorOccurredHandler ErrorOccurred;

        public void OnErrorOccurred(ErrorEventArgs args)
        {
            ErrorOccurredHandler handler = ErrorOccurred;
            if (handler != null) handler(this, args);
        }

        public void OnDeviceStatusChanged(DeviceStatusEventArgs args)
        {
            DeviceStatusChangedHandler handler = DeviceStatusChanged;
            if (handler != null) handler(this, args);
        }

        public void OnNewImage(NewImageEventArgs args)
        {
            ImageReceivedHandler handler = NewImageReceived;
            if (handler != null) handler(this, args);
        }

        public void Init()
        {
            if (!_initialized)
            {
                var check = new tagHWRecvCheck();
                check.bAllow = true;
                check.szName = DeviceId;

                var checkPtr = Marshal.AllocHGlobal(Marshal.SizeOf(check));
                Marshal.StructureToPtr(check, checkPtr, true);

                var checkList = new tagHWRecvCheckList();
                checkList.bAllowOther = true;
                checkList.num = 1;
                checkList.check = checkPtr;

                var parm = new tagHWRecvParam();
                _callBack = CallBack;
                parm.pCallBack = _callBack;
                parm.checklist = checkList;
                parm.port = 5335;
                var result = NativeMethods.HWDataRecvInit(parm);
                System.Diagnostics.Debug.Assert(result == tagHWRecvRet.HWRECV_RET_OK);
                _initialized = true;
                Marshal.FreeHGlobal(checkPtr);
            }
        }

        public void Start()
        {
            if (!_initialized)
            {
                throw new InvalidOperationException("not initialized");
            }

            var result = NativeMethods.HWDataRecvBegin();
            System.Diagnostics.Debug.Assert(result == tagHWRecvRet.HWRECV_RET_OK);
        }

        public void Dispose()
        {
            if (_initialized)
            {
                NativeMethods.HWDataRecvClose();
            }
        }


        private bool CallBack(tagHWRecvEvent args)
        {
            switch (args.type)
            {
                case tagHWRecvEventType.HWRECV_EVENT_LINK:
                case tagHWRecvEventType.HWRECV_EVENT_BREAK:
                    {
                        var data = (tagHWRecvDevice)Marshal.PtrToStructure(args.data, typeof(tagHWRecvDevice));
                        var ip = new System.Net.IPAddress(data.ip);
                        

                        var status = args.type == tagHWRecvEventType.HWRECV_EVENT_LINK
                                         ? DeviceStatus.Connected
                                         : DeviceStatus.DisConnected;

                        var a = new DeviceStatusEventArgs(data.szName, ip, status);
                        OnDeviceStatusChanged(a);
                    }
                    break;
                case tagHWRecvEventType.HWRECV_EVENT_EXCEPT:
                    {
                        var data = (tagHWRecvException)Marshal.PtrToStructure(args.data, typeof(tagHWRecvException));
                        var t = data.type == tagHWRecvExceptionType.HWRECV_EXCEPT_WARNING
                                    ? ErrorType.Warning
                                    : ErrorType.Error;
                        var a = new ErrorEventArgs(t, data.szInfo);
                        OnErrorOccurred(a);
                    }
                    break;
                case tagHWRecvEventType.HWRECV_EVENT_DATA_T:
                    {
                        var data = (tagHWRecvDataT)Marshal.PtrToStructure(args.data, typeof(tagHWRecvDataT));
                        var bitmapDatas = CopyImageBuffers(data);
                        var time = new DateTime(data.timePass.wYear,
                                                data.timePass.wMonth,
                                                data.timePass.wDay,
                                                data.timePass.wHour,
                                                data.timePass.wMinute,
                                                data.timePass.wSecond);
                        var reason = CaptureReason.Normal;
                        if (data.bWrongWay)
                        {
                            reason = CaptureReason.WrongDirection;
                        }
                        var a = new NewImageEventArgs()
                                    {
                                        BitmapDatas = bitmapDatas,
                                        CaptureTime = time,
                                        DeviceId = data.szName,
                                        LanNo = data.sDrivewayNo,
                                        CaptureReason = reason,
                                        Speed = data.sSpeed
                                    };
                        this.OnNewImage(a);
                    }
                    break;
                case tagHWRecvEventType.HWRECV_EVENT_DATA_V:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            return true;
        }

        private static List<byte[]> CopyImageBuffers(tagHWRecvDataT data)
        {
            var bitmapDatas = new List<byte[]>();
            if (data.dwLenFeature > 0 && data.pPicFeature != IntPtr.Zero)
            {
                var buffer = new byte[data.dwLenFeature];
                Marshal.Copy(data.pPicFeature, buffer, 0, buffer.Length);
                bitmapDatas.Add(buffer);
            }

            if (data.dwLenExt1 > 0 && data.pPicExt1 != IntPtr.Zero)
            {
                var buffer = new byte[data.dwLenExt1];
                Marshal.Copy(data.pPicExt1, buffer, 0, buffer.Length);
                bitmapDatas.Add(buffer);
            }

            if (data.dwLenExt2 > 0 && data.pPicExt2 != IntPtr.Zero)
            {
                var buffer = new byte[data.dwLenExt2];
                Marshal.Copy(data.pPicExt2, buffer, 0, buffer.Length);
                bitmapDatas.Add(buffer);
            }
            if (data.dwLenExt3 > 0 && data.pPicExt3 != IntPtr.Zero)
            {
                var buffer = new byte[data.dwLenExt1];
                Marshal.Copy(data.pPicExt3, buffer, 0, buffer.Length);
                bitmapDatas.Add(buffer);
            }
            return bitmapDatas;
        }
    }
}
